{*
  Object Pascal simple Hard Disk SMART technology manipulation routines
  @Author  Maciej Zagozda (1998-2008)
  @Licence Microsoft Reciprocal License (Ms-RL)
  @Website http://www.codeplex.com/intelitechdelphirtns/
  @Version 2008/04/20 Added documentation support
}

unit DiskRoutinesSMART;

interface

uses
  Windows, Messages, SysUtils, Classes, Contnrs, Graphics, Controls, Forms, Dialogs, StdCtrls;

type

  TSmartData = array[0..527] of Byte;

  TDiskInfo = class
  private
    hdrive: Cardinal;
    dwBytesReturned: DWORD;
    ipar: array[0..31] of Byte;
    opar: TSmartData;
    fPhysicalDrive: Byte;
    fSmartDataVariables: TSmartData;

    fSmartSummary: TStringList;

    fSpinUpTime: Longint;
    fStartStopCount: Longint;
    fRellocatedSectorsCount: Longint;
    fReadChannelMargin: Longint;
    fSeekErrorRate: Longint;
    fSeekTimePerformance: Longint;
    fPowerOnMinutes: Longint;
    fSpinRetryCount: Longint;
    fRecalibrationRetries: Longint;
    fDevicePowerCycleCount: Longint;
    fLoadUnloadCycleCount: Longint;
    fTemperature: Longint;
    fReallocationEvenCount: Longint;
    fCurrentPendingSectorCount: Longint;
    fUncorrectableSectorCount: Longint;
    fUDMACRCErrorCount: Longint;
    fWriteErrorRate: Longint;

    fSectorsPerCluster: DWORD; // holds the sectors per cluster
    fBytesPerSector: DWORD; // holds the bytes per sector
    fFreeClusters: DWORD; // holds the number of free clusters
    fClusters: DWORD; // holds the total number of disk clusters
    fDiskFreeSpace: Int64; // holds the total free space
    fTotalDiskSpace: Int64; // holds total disk capacity
    fDrive: AnsiString;
    fDriveType: AnsiString;
    fVolumeName: AnsiString;
    fSerialNumber: AnsiString;
    fFileSystemType: AnsiString;

    function GetDriveTyp :AnsiString;
    procedure SetDrive(value:AnsiString);

    function GetSmartSummary: TStringList;
  public
    constructor Create;
    destructor Destroy;
    procedure RefreshSmartStatus;
  published
    property PhysicalDrive: Byte read fPhysicalDrive;

    property SmartSummary: TStringList read GetSmartSummary;

    property SpinUpTime: Longint read fSpinUpTime;
    property StartStopCount: Longint read fStartStopCount;
    property RellocatedSectorsCount: Longint read fRellocatedSectorsCount;
    property ReadChannelMargin: Longint read fReadChannelMargin;
    property SeekErrorRate: Longint read fSeekErrorRate;
    property SeekTimePerformance: Longint read fSeekTimePerformance;
    property PowerOnMinutes: Longint read fPowerOnMinutes;
    property SpinRetryCount: Longint read fSpinRetryCount;
    property RecalibrationRetries: Longint read fRecalibrationRetries;
    property DevicePowerCycleCount: Longint read fDevicePowerCycleCount;
    property LoadUnloadCycleCount: Longint read fLoadUnloadCycleCount;
    property Temperature: Longint read fTemperature;
    property ReallocationEvenCount: Longint read fReallocationEvenCount;
    property CurrentPendingSectorCount: Longint read fCurrentPendingSectorCount;
    property UncorrectableSectorCount: Longint read fUncorrectableSectorCount;
    property UDMACRCErrorCount: Longint read fUDMACRCErrorCount;
    property WriteErrorRate: Longint read fWriteErrorRate;


    property Drive :string read FDrive write setdrive;
    property SectorsPerCluster :DWORD read FSectorsPerCluster;
    property BytesPerSector :DWORD read FBytesPerSector;
    property FreeClusters :DWORD read FFreeClusters;
    property Clusters :DWORD read FClusters;
    property DiskFreeSpace :Int64 read FDiskFreeSpace;
    property TotalDiskSpace :Int64 read FTotalDiskSpace;
    property DriveType :string read GetDriveTyp;
    property VolumeName :string read FVolumeName;
    property SerialNumber :string read FSerialNumber;
    property FileSystemType :string read FFileSystemType;
  end;

implementation

constructor TDiskInfo.Create;
begin
  inherited;
  fPhysicalDrive := 0;
    setdrive('C');
  fSmartSummary := TStringList.Create;
end;

destructor TDiskInfo.Destroy;
begin
  fSmartSummary.Free;
  inherited;
end;

function TDiskInfo.GetSmartSummary: TStringList;
begin
  RefreshSmartStatus;

  with fSmartSummary do
  begin
    Add('Spin Up Time: ' + IntToStr(SpinUpTime));
    Add('Start/Stop Count: ' + IntToStr(StartStopCount));
    Add('Rellocated Sectors Count: ' +IntToStr(RellocatedSectorsCount));
    Add('Read Channel Margin: ' + IntToStr(ReadChannelMargin));
    Add('Seek Error Rate: ' + IntToStr(SeekErrorRate));
    Add('Seek Time Performance: ' + IntToStr(SeekTimePerformance));
    Add('Power On Minutes: ' + IntToStr(PowerOnMinutes));
    Add('Spin Retry Count: ' + IntToStr(SpinRetryCount));
    Add('Recalibration Retries: '  + IntToStr(RecalibrationRetries));
    Add('Power Cycle Count: ' + IntToStr(DevicePowerCycleCount));
    Add('Unload Cycle Count: ' + IntToStr(LoadUnloadCycleCount));
    Add('Temperature: ' + IntToStr(Temperature));
    Add('Reallocation Even Count: ' + IntToStr(ReallocationEvenCount));
    Add('Pending Sector Count: ' + IntToStr(CurrentPendingSectorCount));
    Add('Bad Sector Count: ' + IntToStr(UncorrectableSectorCount));
    Add('CRC Error Count: ' + IntToStr(UDMACRCErrorCount));
    Add('Write Error Rate: ' + IntToStr(WriteErrorRate));
  end;

  Result := fSmartSummary;
end;

procedure TDiskInfo.RefreshSmartStatus;
begin
  ipar[0]  := 0;
  ipar[1]  := $02;
  ipar[2]  := 0;
  ipar[3]  := 0;
  ipar[4]  := $d0;
  ipar[5]  := $01;
  ipar[6]  := $01;
  ipar[7]  := $4f;
  ipar[8]  := $c2;
  ipar[9]  := $a0;
  ipar[10] := $b0;
  ipar[11] := 0;
  ipar[12] := 0;
  ipar[13] := 0;
  ipar[14] := 0;
  ipar[15] := 0;
  ipar[16] := $8c;
  ipar[17] := $fd;
  ipar[18] := $14;
  ipar[19] := 0;
  ipar[20] := 0;
  ipar[21] := $02;
  ipar[22] := 0;
  ipar[23] := 0;
  ipar[24] := $03;
  ipar[25] := 0;
  ipar[26] := 0;
  ipar[27] := 0;
  ipar[28] := $03;
  ipar[29] := 0;
  ipar[30] := 0;
  ipar[31] := 0;

  hdrive  := CreateFile(PChar(Format('\\.\PhysicalDrive%d', [fPhysicalDrive] )), 3221225472, 3, nil, 3, 0, 0);
  DeviceIoControl(hdrive, $0007C088, @ipar, 32, @opar, 528, dwBytesReturned, nil);
  CloseHandle(hdrive);
  fSmartDataVariables := opar;


  fSpinUpTime := fSmartDataVariables[24] * 256 + fSmartDataVariables[23];
  fStartStopCount := fSmartDataVariables[36] * 256 + fSmartDataVariables[35];
  fRellocatedSectorsCount := fSmartDataVariables[48] * 256 + fSmartDataVariables[47];
  fReadChannelMargin := fSmartDataVariables[60] * 256 + fSmartDataVariables[59];
  fSeekErrorRate := fSmartDataVariables[72] * 256 + fSmartDataVariables[71];
  fSeekTimePerformance := fSmartDataVariables[84] * 256 + fSmartDataVariables[83];
  fPowerOnMinutes := fSmartDataVariables[96] * 256 + fSmartDataVariables[95];
  fSpinRetryCount := fSmartDataVariables[108] * 256 + fSmartDataVariables[107];
  fRecalibrationRetries := fSmartDataVariables[120] * 256 + fSmartDataVariables[119];
  fDevicePowerCycleCount := fSmartDataVariables[132] * 256 + fSmartDataVariables[131];
  fLoadUnloadCycleCount := fSmartDataVariables[156] * 256 + fSmartDataVariables[155];
  fTemperature := fSmartDataVariables[168] * 256 + fSmartDataVariables[167];
  fReallocationEvenCount := fSmartDataVariables[192] * 256 + fSmartDataVariables[191];
  fCurrentPendingSectorCount := fSmartDataVariables[204] * 256 + fSmartDataVariables[203];
  fUncorrectableSectorCount := fSmartDataVariables[216] * 256 + fSmartDataVariables[215];
  fUDMACRCErrorCount := fSmartDataVariables[228] * 256 + fSmartDataVariables[227];
  fWriteErrorRate := fSmartDataVariables[240] * 256 + fSmartDataVariables[239];
end;

procedure TDiskInfo.SetDrive(value:string);
var
  fulldrive: ShortString;
  tmp_drive: array[0..2] of Char;
  Tmp_SectorsPerCluster, Tmp_BytesPerSector, Tmp_FreeClusters, Tmp_Clusters: DWORD;
  VolName:array[0..255] of Char;
  SerialNumber:DWORD;                // holds the serial number
  MaxCLength        :DWORD;                  // holds the maximum file component length
  FileSysFlag       :DWORD;                 // holds file system flags
  FileSysName       :array[0..255] of Char; // holds the name of the file system
  //total             :int64;
    s                 :string;
begin
  fdrive:=value;
  fulldrive:=value + ':\';
  strpcopy(tmp_drive,fulldrive);
  if GetDiskFreeSpace(tmp_drive,Tmp_SectorsPerCluster,Tmp_BytesPerSector, Tmp_FreeClusters,Tmp_Clusters) then
  begin
    FSectorsPerCluster:=Tmp_SectorsPerCluster;
    FBytesPerSector:=Tmp_BytesPerSector;
    FFreeClusters:=Tmp_FreeClusters;
    FClusters:=Tmp_Clusters;
    FDiskFreeSpace:=int64(Tmp_FreeClusters) * int64(Tmp_BytesPerSector) * int64(Tmp_SectorsPerCluster);
    FTotalDiskSpace:=int64(Tmp_Clusters) * int64(Tmp_BytesPerSector) * int64(Tmp_SectorsPerCluster);
  end;

  {retrieve the volumn information}
  GetVolumeInformation(tmp_drive, VolName, 255, @SerialNumber, MaxCLength,
     FileSysFlag, FileSysName, 255);

  FVolumeName:=VolName;
  FSerialNumber:=IntToHex(SerialNumber,8);
  FFileSystemType:=FileSysName;
end;

function TDiskInfo.GetDriveTyp :string;
var
  fulldrive: string[3];
  tmp_drive: array[0..2] of char;
begin
  fulldrive:=fdrive + ':\';
  strpcopy(tmp_drive,fulldrive);
  {retrieve the drive type}
  case GetDriveType(tmp_drive) of
    DRIVE_UNKNOWN     :result:='No Type Information';
    DRIVE_NO_ROOT_DIR :result:='Root Directory does not exist';
    DRIVE_REMOVABLE   :result:='Removable';
    DRIVE_FIXED       :result:='Fixed';
    DRIVE_REMOTE      :result:='Remote';
    DRIVE_CDROM       :result:='CDROM';
    DRIVE_RAMDISK     :result:='RamDisk';
  end;
end;

end.
